package com.pacific.uumconsole.common;

import java.io.IOException;

import org.springframework.util.ObjectUtils;

public class StringUtil {
    
    
    public static char[] HTTP_SPECIAL_CHARS = {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', ' ', '<', '>', '#', '%', '"', '{', '}', '|', '\\', '^', '[', ']', '\'' };
    
    public static String toHexString(byte [] hash) {
        if (hash == null) return "";
        return toHexString(hash, hash.length * 2);
    }

    /** Length limited toHexString().
      * @param len the maximum length of the resulting hex string. NOTE: to limit the portion of
      * the input to be processed, pass (len * 2). For example, to toHexString() the first 4 bytes of
      * a byte array, pass 8 to limit the output string to 8 characters.
      * @return hex string
      */
    public static String toHexString(byte [] hash, int len) {
        if (hash == null)
            return "";
        int max = hash.length * 2;
        if( len > max) len = max;

        char buf[] = new char[len];
        for (int i=0, j=0; j<len; i++) {
            int x = (int)hash[i] & 255;
            int n = x >> 4;
            buf[j++] = (n < 10)? (char)(n + (int)'0') : (char)(n - 10 + (int)'a');
            n = x & 15;
            buf[j++] = (n < 10)? (char)(n + (int)'0') : (char)(n - 10 + (int)'a');
        }
        return new String(buf);
    }

    public static StringBuffer hexDump(byte[] buf) {
        StringBuffer buffer = new StringBuffer();
        StringBuffer s1 = new StringBuffer();
        int i = 0;

        while (true) {
            if (i % 16 == 0) {
                int pos = buffer.length();
                buffer.append(Integer.toHexString(i));
                while (buffer.length() - pos < 12)
                    buffer.append(' ');
            }
            if (buf[i] >= 0 && buf[i] < 16)
                buffer.append('0');
            buffer.append(Integer.toHexString(buf[i] & 0xff)).append(' ');

            s1.append(
                (buf[i] <= 31) ? ' ' : (char) buf[i]);
            i++;
            if (i % 16 == 0 || i == buf.length) {
                for (int j = i % 16; j != 0; j = (j + 1) % 16)
                    buffer.append("   ");
                buffer.append("  ").append(s1.toString()).append('\n');
                s1.setLength(0);
            }

            if (i == buf.length)
                break;
        }
        return buffer;
    }

    public static byte [] fromHexString(String s) {
        if (s == null)
            return null;
        s = s.toLowerCase();
        int i, j=0, len = s.length();
        byte [] result = new byte[len/2];
        for (i=0; i<len; i+=2) {
            char chi = s.charAt(i), clo = s.charAt(i+1);
            int nibhi = (chi <= '9')? (int)(chi - '0') : (int)(chi-'a')+10;
            int niblo = (clo <= '9')? (int)(clo - '0') : (int)(clo-'a')+10;
            result[j++] = (byte)((nibhi << 4) + niblo);
        }
        return result;
    }

    public static boolean compareByteArrays(byte[] b1, byte[] b2) {
        if (b1 == null && b2 == null) return true;
        if (b1 == null || b2 == null) return false;
        if (b1.length != b2.length) return false;
        for (int i = 0; i < b1.length; i++) {
            if (b1[i] != b2[i]) return false;
        }
        return true;
    }
    public static boolean compareByteArrays(byte[] b1, byte[] b2, int len) {
        if (b1 == null && b2 == null) return true;
        if (b1 == null || b2 == null) return false;
        if (b1.length < len || b2.length < len) return false;
        for (int i = 0; i < len; i++) {
            if (b1[i] != b2[i]) return false;
        }
        return true;
    }
    
    public static int toInt(byte[] src, int offset) {
        return (((src[offset++] & 0xff) << 24) | ((src[offset++] & 0xff) << 16)
                | ((src[offset++] & 0xff) << 8) | ((src[offset++] & 0xff)));
    }
    
    public static void writeInt(int v, byte[] dest, int index){
        dest[index++] = (byte)((v >>> 24) & 0xFF);
        dest[index++] = (byte)((v >>> 16) & 0xFF);
        dest[index++] = (byte)((v >>>  8) & 0xFF);
        dest[index++] = (byte)((v >>>  0) & 0xFF);
    }
    
    public static void writeShort(int v, byte[] dest, int index){
        dest[index++] = (byte)((v >>>  8) & 0xFF);
        dest[index++] = (byte)((v >>>  0) & 0xFF);
        
        
    }

    /**
     * Returns the number of bytes required to hold UTF-8 encoded version of
     * the given UTF-16 character array
     * @param charArray The character array whose length to determine
     * @return Number of bytes required for output buffer
     */
    public static int getUTF8Length(char[] charArray) {
        int count = 0;
        for (int i = 0; i < charArray.length; ++i) {
            char c = charArray[i];

            if (c < 0x80) {
                count++;
            } else if (c < 0x800) {
                count += 2;
            } else {
                short surrogate = (short)(((short)c) & 0xfc00);
                if ((surrogate ^ 0xdc00) == 0)
                    count += 4;  // surrogate in range 0xdc00..0xdfff
                else if ((surrogate ^ 0xd800) != 0)
                    count += 3;  // not a surrogate in range 0xd800..0xdbff
            }
        }

        return count;
    }

    /**
     * Converts a UTF-16 encoded character array into UTF-8 encoded array
     * @param charArray The UTF-16 character array to convert
     * @param data Output buffer to receive the UTF-8 encoded data. This must
     *           be at least as many bytes as what getUTF8Length returns for the
     *           charArray
     */
    public static void convertUTF16ToUTF8(char[] charArray, byte[] data) {
        int index = 0;
        for (int i = 0; i < charArray.length; ++i) {
            char c = charArray[i];

            if (c < 0x80) {
                data[index++] = (byte)c;
            } else if (c < 0x800) {
                data[index++] = (byte)((byte)0xc0 | (byte)(((short)c) >> 6));
                data[index++] = (byte)((byte)0x80 | (byte)(((short)c) & 0x3f));
            } else {
                short surrogate = (short)(((short)c) & 0xfc00);
                if ((surrogate ^ 0xdc00) == 0) {
                    // invalid occurrence of a surrogate in range 0xdc00..0xdfff
                    data[index++] = '?';
                } else if ((surrogate ^ 0xd800) == 0) {
                    // surrogate in range 0xd800..0xdbff
                    // next element must be a surrogate in range 0xdc00..0xdfff
                    if (i + 1 >= charArray.length)
                        return;

                    ++i;
                    char c2 = charArray[i];
                    surrogate = (short)(((short)c2) & 0xfc00);
                    if ((surrogate ^ 0xdc00) != 0) {
                        // no surrogate in range 0xdc00..0xdfff found
                        data[index++] = '?';
                    } else {
                        int bigC = (((int)(c - 0xd800)) << 10) + ((int)(c2 - 0xdc00)) + 0x10000;

                        data[index++] = (byte)(0xf0 | (bigC >> 18));
                        data[index++] = (byte)(0x80 | ((bigC >> 12) & 0x3f));
                        data[index++] = (byte)(0x80 | ((bigC >> 6) & 0x3f));
                        data[index++] = (byte)(0x80 | (bigC & 0x3f));
                    }
                } else {
                    data[index++] = (byte)(0xe0 | (c >> 12));
                    data[index++] = (byte)(0x80 | ((c >> 6) & 0x3f));
                    data[index++] = (byte)(0x80 | (c & 0x3f));
                }
            }
        }
    }

    /**
     * Java's implementation of getBytes("UTF-8") is slow and apparently
     * buggy on some J2ME devices (Samsung Loches/Tocco) so do the encoding
     * ourselves. On Android's the Java implementation is 20 timer slower
     * than this code
     */
    public static byte[] getUTF8BytesFast(String s) {
        char[] charArray = s.toCharArray();
        byte[] data = new byte[getUTF8Length(charArray)];
        try {
            convertUTF16ToUTF8(charArray, data);
        } catch (IndexOutOfBoundsException e) {
            // Invalid UTF-16 data could cause IndexOutOfBoundsException, just ignore it for now
        }
        return data;
    }

    /**
     * This method is a workaround for a bug in Samsung Loches/Tocco devices,
     * sometimes String.getBytes("UTF-8") returns an empty byte array instead of 
     * correct bytes.
     */
    public static byte[] getUTF8Bytes(String s) throws IOException {
        byte[] data = s.getBytes("UTF-8");
      //#ifdef J2ME
      //# if (data == null || data.length < s.length()) {
      //#   // in UTF-8, all characters take at least one byte - so if we receive
      //#   // less bytes than there are characters, we have corrupted byte array.
      //#   ByteArrayOutputStream baos = new ByteArrayOutputStream();
      //#   OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");
      //#   for (int i=0 ; i < s.length() ; i++) {
      //#       osw.write(s.charAt(i));
      //#   }
      //#   data = baos.toByteArray();
      //# }
      //#endif
        return data;
    }



    //Escaping/unescaping for JID disallowed characters according to XEP-0106 JID ESCAPING
    public static char[] PROHIBIT = new char[]{' ','\"', '&', '\'', '/', ':', '<', '>', '@','\\'};
   
    public static boolean needEscape(char c){
        boolean isFind = false;
        for ( int i = 0; i < PROHIBIT.length; i++ ){
            if( c == PROHIBIT[i] ){
                isFind = true;
                break;
            } 
        }
        return isFind;        
    }
    
    public static char unEscape(String s) {
        for( int i=0; i < PROHIBIT.length; i++ ){
            if(s.equals(Integer.toHexString(PROHIBIT[i]))){
                return PROHIBIT[i];
            }
        } 
        //This should not happen.
        return 0;
    }
   
    public static String escapedJIDString(String userId) {
        if( userId == null ){
            return null;
        }
        StringBuffer sb = new StringBuffer();
        for( int i = 0; i < userId.length(); i++ ){
            char c = userId.charAt(i);
            if( needEscape(c) ){
                sb.append('\\');
                sb.append(Integer.toHexString(c));                
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    
    public static String unescapedJIDString(String escapedJid){        
       if( escapedJid == null ){
         return null;
       }
       StringBuffer sb = new StringBuffer();
       for( int i = 0; i < escapedJid.length(); ){
          char c = escapedJid.charAt(i);
          if( c == '\\' ){
              String escapedchar = escapedJid.substring(i + 1, i + 3);
              if( unEscape(escapedchar) != 0){
                  sb.append(unEscape(escapedchar));
                  i += 3;  
              } else {
                  sb.append(c);
                  i++;
              }             
          } else {
            sb.append(c);
            i++;
          }
      }
      return sb.toString();
    }

    public static String nvl(String a, String b) {
        return a == null ? b : a;
    }

//#ifndef J2ME    
    public static String escapeAmpersandFromUrl(String str) {
        if (!str.contains("&")) {
            return str;
        }
        boolean first = false;
        int indexOffirst = -1;
        StringBuilder temp = new StringBuilder();
        for (int i =0 ; i < str.length();i++) {
            temp.append(str.charAt(i));
            if(str.charAt(i) == '&') {
                if (!first) {
                    first = true;
                    indexOffirst = temp.length()-1;
                } else {
                    temp.replace(indexOffirst, indexOffirst+1, "%26");
                    indexOffirst = temp.length()-1;
                }
            } else if(str.charAt(i) == '=') {
                first = false;
            } 
        }
        if (first) {
            temp.replace(indexOffirst, indexOffirst+1, "%26");
        }
        return temp.toString();
    }
//#endif

    /** Masks user private information - e.g., for logging. */
    public static String mask(String src, int numCharsVisibleFront, int numCharsVisibleBack) {
        if (src == null || src.length() == 0)
            return "";
        
        if (numCharsVisibleFront < 0) numCharsVisibleFront = 0;
        if (numCharsVisibleBack < 0) numCharsVisibleBack = 0;
        
        final int len = src.length();
        final StringBuilder tmp = new StringBuilder(src);
        if (len <= numCharsVisibleFront + numCharsVisibleBack) {
            tmp.setCharAt(len / 2, '*'); // make sure at least 1 star appears
        } else {
            for (int i = numCharsVisibleFront; i < len - numCharsVisibleBack; i++)
                tmp.setCharAt(i, '*');
        }
        return tmp.toString();
    }

    private static final int PHONE_FRONT_CHAR_COUNT = 0;
    private static final int PHONE_BACK_CHAR_COUNT = 4;

    /** Masks phone number - shows last 4 digits of the phone number. */
    public static String maskPhone(String phone) {
        return mask(phone, PHONE_FRONT_CHAR_COUNT, PHONE_BACK_CHAR_COUNT);
    }

    /** Masks contact display name - shows first 2 and last 2 chars. */
    public static String maskName(String name) {
        return mask(name, 2, 2);
    }

    /** Masks email address - shows max of 4 chars before the domain (domain itself is not masked). */
    public static String maskEmail(String emailAddress) {
        if (emailAddress == null || emailAddress.length() == 0)
            return "";
        int index = emailAddress.lastIndexOf('@');
        if (index > 0)
            return mask(emailAddress, Math.min(4, index - 1), emailAddress.length() - index);
        else
            return mask(emailAddress, 4, 0);
    }

    /** Masks email summary - shows first 8 chars and last char. */
    public static String maskEmailSummary(String emailSummary) {
        return mask(emailSummary, 8, 1);
    }

    /**
     * Masks the special phone_lookup value used in Ping events table - shows first 4 chars.
     * This conforms to the {@link #maskPhone(String)} behavior, as the phone_lookup is really
     * the last 4 significant digits of the phone number, reversed.
     */
    public static String maskPhoneLookup(String phoneLookup) {
        // Yes, we are deliberately reversing the front/back values.
        return mask(phoneLookup, PHONE_BACK_CHAR_COUNT, PHONE_FRONT_CHAR_COUNT);
    }
    
    public static boolean isEmpty(String str){
    	return null == str || "".equals(str.trim()) ? true : false;
    }
    
	/**
	 * Convenience method to return a String array as a delimited (e.g. CSV)
	 * String. E.g. useful for <code>toString()</code> implementations.
	 * @param arr the array to display
	 * @param delim the delimiter to use (probably a ",")
	 * @return the delimited String
	 */
	public static String arrayToDelimitedString(Object[] arr, String delim) {
		if (ObjectUtils.isEmpty(arr)) {
			return "";
		}
		if (arr.length == 1) {
			return ObjectUtils.nullSafeToString(arr[0]);
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			if (i > 0) {
				sb.append(delim);
			}
			sb.append(arr[i]);
		}
		return sb.toString();
	}

	/**
	 * Convenience method to return a String array as a CSV String.
	 * E.g. useful for <code>toString()</code> implementations.
	 * @param arr the array to display
	 * @return the delimited String
	 */
	public static String arrayToCommaDelimitedString(Object[] arr) {
		return arrayToDelimitedString(arr, ",");
	}


}

